
package it.lucci.ricochet;

import it.lucci.ricochet.entities.Ball;
import it.lucci.ricochet.entities.Pad;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;

/**
 * @author Gabriele Lucci
 * @version 1.0
 * Copyright 2012, Gabriele Lucci
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

public class Game implements Runnable, MouseMotionListener, MouseListener {
    
    private Ball ball; 
    private Pad pad;
    public Dimension edges;
    public long score;
    private Thread gameThread;
    private boolean running;
    /**
     * 
     * @param p il pannello che disegna questo gioco
     */
    public Game(JPanel p) {
        this.compute(p);
    }
    
    @Override
    public void run() {
        while (true) {
            // se la palla si muove
            if (ball.isMoving()) {
                ball.move();    //muovi la palla
                this.checkCollision();  //gestisci collisioni
            }
            else {
                ball.setX(pad.getX()+(pad.getWidth()/2)-(ball.getDiameter()/2));
                ball.setY(pad.getY()-ball.getDiameter());
            }
            pause(5);   // aggiorna ogni 5 millis
        }
    }
    
    private void checkCollision(){
        // controlla se la palla:
        //*ha colpito il bordo superiore
        if (ball.highestPoint().y <= 0) { 
            ball.setSpeedY(-(ball.getSpeedY()));
        }
        //*ha colpito il bordo sinistro
        if (ball.leftmostPoint().x <= 0) {   
            ball.setSpeedX(-(ball.getSpeedX()));
            ball.setX(ball.getDiameter()); 
        }
        //*ha colpito il bordo destro
        if (ball.rightmostPoint().x >= edges.width) {
            ball.setSpeedX(-(ball.getSpeedX()));
            ball.setX(edges.width-ball.getDiameter()); 
        }
        //*ha colpito il bordo inferiore 
        if (ball.lowestPoint().y >= edges.height+50) {
            pause(1000);
            score = 0;
            ball.setMoving(false);
        }
        //*ha colpito il pad
        else if (ball.hasHit(pad)) {
            ball.setSpeedY(-(ball.getSpeedY()));
            score++;
        }
    }
    
    /**
     * Calcola e imposta i valori dei vari attributi da disegnare in base alle 
     * dimensioni del pannello che disegna questo Game.
     * @param  p il pannello target
     */  
    private void compute(JPanel p){
        edges = p.getPreferredSize();
        double w = edges.width;
        double h = edges.height;
        // impostazioni pad
        pad = new Pad(20,(h-(h/15)), (w/4), (h/70));
        // impostazioni palla
        ball = new Ball();
        ball.setDiameter(pad.getHeight()*1.9);
        ball.setDefaultSpeedX(-(w/275));
        ball.setDefaultSpeedY(-(h/275));
    }
    
    private void pause(long millis){
        try {
            Thread.sleep(millis);
        } catch (InterruptedException ex) { 
            System.err.println(ex); 
        }
    }
    
    public void startGame(){
        if(gameThread == null || !running){
            gameThread = new Thread(this, "Game Thread");
            gameThread.start();
            running = true;
        }
    }
    
    /**
     * 
     * @return 
     */
    public Ball getBall() {
        return ball;
    }
    /**
     * 
     * @param ball 
     */
    public void setBall(Ball ball) {
        this.ball = ball;
    }
    /**
     * 
     * @return 
     */
    public Pad getPad() {
        return pad;
    }
    /**
     * 
     * @param pad 
     */
    public void setPad(Pad pad) {
        this.pad = pad;
    }

    /**
     * Metodo che viene eseguito quando un pulsante del mouse viene premuto.
     * @param me 
     */
    @Override
    public void mousePressed(MouseEvent me) {
        if (me.getButton()==MouseEvent.BUTTON1 && !ball.isMoving()) {
            // partenza
            ball.setSpeedX(ball.getDefaultSpeedX());
            ball.setSpeedY(ball.getDefaultSpeedY());
            ball.setMoving(true);
            return;
        } 
        if (me.getButton()==MouseEvent.BUTTON3 && !ball.isMoving()) {
            ball.setSpeedX(-ball.getDefaultSpeedX());
            ball.setSpeedY(ball.getDefaultSpeedY());
            ball.setMoving(true);
            return;
        }
        if (me.getButton()==MouseEvent.BUTTON2 && ball.isMoving()) {
            ball.setMoving(false);
        }
    }

    @Override
    public void mouseReleased(MouseEvent me) { }

    @Override
    public void mouseEntered(MouseEvent me) { }

    @Override
    public void mouseExited(MouseEvent me) { }

    @Override
    public void mouseDragged(MouseEvent me) {
        mouseMoved(me); 
    }
    
    /**
     * Metodo che viene eseguito quando mouse viene mosso dentro 
     * questo componente.
     * @param me 
     */
    @Override
    public void mouseMoved(MouseEvent me) {
        pad.setX(me.getX()-pad.getWidth()/2);   
    }
    
    @Override
    public void mouseClicked(MouseEvent me) { }
    
}
